package com.carey.blog.jsonrpc.impl;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONArray;
import org.json.JSONObject;

import com.carey.blog.action.StatusCodes;
import com.carey.blog.framework.latke.Keys;
import com.carey.blog.framework.latke.action.ActionException;
import com.carey.blog.framework.latke.action.util.PageCaches;
import com.carey.blog.framework.latke.action.util.Paginator;
import com.carey.blog.framework.latke.model.Pagination;
import com.carey.blog.framework.latke.repository.Query;
import com.carey.blog.framework.latke.repository.SortDirection;
import com.carey.blog.framework.latke.repository.Transaction;
import com.carey.blog.jsonrpc.AbstractGAEJSONRpcService;
import com.carey.blog.model.File;
import com.carey.blog.repository.FileRepository;
import com.carey.blog.repository.impl.FileGAERepository;
import com.carey.blog.util.Users;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

/**
 * File service for JavaScript client.
 * 
 */
public final class FileService extends AbstractGAEJSONRpcService {

	/**
	 * Logger.
	 */
	private static final Logger LOGGER = Logger.getLogger(FileService.class
			.getName());
	/**
	 * Blob service.
	 */
	private BlobstoreService blobstoreService = BlobstoreServiceFactory
			.getBlobstoreService();
	/**
	 * File repository.
	 */
	private FileRepository fileRepository = FileGAERepository.getInstance();
	/**
	 * User utilities.
	 */
	private Users userUtils = Users.getInstance();

	/**
	 * Gets the file with the specified request json object, http servlet
	 * request and response.
	 * 
	 * @param requestJSONObject
	 *            the specified request json object, for example,
	 * 
	 *            <pre>
	 * {
	 *     "paginationCurrentPageNum": 1,
	 *     "paginationPageSize": 20,
	 *     "paginationWindowSize": 10
	 * }
	 * </pre>
	 * @param request
	 *            the specified http servlet request
	 * @param response
	 *            the specified http servlet response
	 * @return for example,
	 * 
	 *         <pre>
	 * {
	 *     "pagination": {
	 *         "paginationPageCount": int,
	 *         "paginationPageNums": [1, 2, 3, 4, 5, int]
	 *     },
	 *     "files": [{
	 *         "fileName": "",
	 *         "fileSize": long,
	 *         "fileDownloadCount": int,
	 *         "fileUploadDate": java.util.Date
	 *     }, ....]
	 * }
	 * </pre>
	 * @throws ActionException
	 *             action exception
	 * @throws IOException
	 *             io exception
	 */
	public JSONObject getFiles(final JSONObject requestJSONObject,
			final HttpServletRequest request, final HttpServletResponse response)
			throws ActionException, IOException {
		final JSONObject ret = new JSONObject();
		if (!userUtils.isLoggedIn()) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return ret;
		}

		try {
			final int currentPageNum = requestJSONObject
					.getInt(Pagination.PAGINATION_CURRENT_PAGE_NUM);
			final int pageSize = requestJSONObject
					.getInt(Pagination.PAGINATION_PAGE_SIZE);
			final int windowSize = requestJSONObject
					.getInt(Pagination.PAGINATION_WINDOW_SIZE);

			final Query query = new Query().setCurrentPageNum(currentPageNum)
					.setPageSize(pageSize)
					.addSort(File.FILE_UPLOAD_DATE, SortDirection.DESCENDING);
			final JSONObject result = fileRepository.get(query);
			final int pageCount = result.getJSONObject(Pagination.PAGINATION)
					.getInt(Pagination.PAGINATION_PAGE_COUNT);

			final JSONObject pagination = new JSONObject();
			ret.put(Pagination.PAGINATION, pagination);
			final List<Integer> pageNums = Paginator.paginate(currentPageNum,
					pageSize, pageCount, windowSize);
			pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
			pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);

			final JSONArray files = result.getJSONArray(Keys.RESULTS);
			for (int i = 0; i < files.length(); i++) { // Remove unused
														// properties
				final JSONObject file = files.getJSONObject(i);
				file.remove(File.FILE_CONTENT_TYPE);
				file.remove(File.FILE_CONTENT);
			}
			ret.put(File.FILES, files);

			ret.put(Keys.STATUS_CODE, StatusCodes.GET_FILES_SUCC);
		} catch (final Exception e) {
			LOGGER.log(Level.SEVERE, e.getMessage(), e);
			throw new ActionException(e);
		}

		return ret;
	}

	/**
	 * Removes a file by the specified request json object.
	 * 
	 * @param requestJSONObject
	 *            the specified request json object, for example,
	 * 
	 *            <pre>
	 * {
	 *     "oId": "",
	 * }
	 * </pre>
	 * @param request
	 *            the specified http servlet request
	 * @param response
	 *            the specified http servlet response
	 * @return for example,
	 * 
	 *         <pre>
	 * {
	 *     "sc": "REMOVE_FILE_SUCC"
	 * }
	 * </pre>
	 * @throws ActionException
	 *             action exception
	 * @throws IOException
	 *             io exception
	 */
	public JSONObject removeFile(final JSONObject requestJSONObject,
			final HttpServletRequest request, final HttpServletResponse response)
			throws ActionException, IOException {
		final JSONObject ret = new JSONObject();
		if (!userUtils.isLoggedIn()) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return ret;
		}
		// TODO: check the file whether is the current user's

		final Transaction transaction = fileRepository.beginTransaction();

		try {
			final String fileId = requestJSONObject.getString(Keys.OBJECT_ID);
			fileRepository.remove(fileId);
			transaction.commit();
			ret.put(Keys.STATUS_CODE, StatusCodes.REMOVE_FILE_SUCC);

			PageCaches.removeAll();
		} catch (final Exception e) {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			LOGGER.log(Level.SEVERE, e.getMessage(), e);
			throw new ActionException(e);
		}

		return ret;
	}

	/**
	 * Gets the upload URL.
	 * 
	 * @param request
	 *            the specified http servlet request
	 * @param response
	 *            the specified http servlet response
	 * @return upload URL
	 * @throws ActionException
	 *             action exception
	 * @throws IOException
	 *             io exception
	 */
	public String getUploadURL(final HttpServletRequest request,
			final HttpServletResponse response) throws ActionException,
			IOException {
		final JSONObject ret = new JSONObject();
		if (!userUtils.isAdminLoggedIn()) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return null;
		}

		return blobstoreService.createUploadUrl("/admin-file-list.do");
	}

	/**
	 * Gets the {@link FileService} singleton.
	 * 
	 * @return the singleton
	 */
	public static FileService getInstance() {
		return SingletonHolder.SINGLETON;
	}

	/**
	 * Private default constructor.
	 */
	private FileService() {
	}

	/**
	 * Singleton holder.
	 * 
	 */
	private static final class SingletonHolder {

		/**
		 * Singleton.
		 */
		private static final FileService SINGLETON = new FileService();

		/**
		 * Private default constructor.
		 */
		private SingletonHolder() {
		}
	}
}
